
import { Mesh, primitives, Vec3 } from 'cc';
import { _decorator, Component, Node, Vec2, utils, gfx, Color, MeshRenderer } from 'cc';
const { ccclass, property, executeInEditMode } = _decorator;


/**
 * @category 3d/primitive
 */

//  import { Vec3 } from '../math';
//  import { IGeometry, IGeometryOptions } from './define';

/**
 * @en
 * The definition of the parameter for building a box.
 * @zh
 * 立方体参数选项。
 */
interface IBoxOptions {
    /**
     * @en
     * Box extent on X-axis.
     * @zh
     * 立方体宽度。
     */
    width?: number;

    /**
     * @en
     * Box extent on Y-axis.
     * @zh
     * 立方体高度。
     */
    height?: number;

    /**
     * @en
     * Box extent on Z-axis.
     * @zh
     * 立方体长度。
     */
    length?: number;
}

const r = new Vec3();
const c0 = new Vec3();
const c1 = new Vec3();
const c2 = new Vec3();
const c3 = new Vec3();
const c4 = new Vec3();
const c5 = new Vec3();
const c6 = new Vec3();
const c7 = new Vec3();

@ccclass('CreateMesh')
@executeInEditMode()
export class CreateMesh extends Component {
    @property
    _p0: Vec3 = new Vec3(1, 1, 1);
    @property
    get p0(): Vec3 { return this._p0; }
    set p0(value: Vec3) {
        this._p0.set(value); this.readyRender = true;
    }

    @property
    _p1: Vec3 = new Vec3(1, 1, 1);
    @property
    get p1(): Vec3 { return this._p1; }
    set p1(value: Vec3) {
        this._p1.set(value); this.readyRender = true;
    }

    @property
    _p2: Vec3 = new Vec3(1, 1, 1);
    @property
    get p2(): Vec3 { return this._p2; }
    set p2(value: Vec3) {
        this._p2.set(value); this.readyRender = true;
    }

    @property
    _p3: Vec3 = new Vec3(1, 1, 1);
    @property
    get p3(): Vec3 { return this._p3; }
    set p3(value: Vec3) {
        this._p3.set(value); this.readyRender = true;
    }

    @property
    _p4: Vec3 = new Vec3(1, 1, 1);
    @property
    get p4(): Vec3 { return this._p4; }
    set p4(value: Vec3) {
        this._p4.set(value); this.readyRender = true;
    }

    @property
    _p5: Vec3 = new Vec3(1, 1, 1);
    @property
    get p5(): Vec3 { return this._p5; }
    set p5(value: Vec3) {
        this._p5.set(value); this.readyRender = true;
    }

    @property
    _p6: Vec3 = new Vec3(1, 1, 1);
    @property
    get p6(): Vec3 { return this._p6; }
    set p6(value: Vec3) {
        this._p6.set(value); this.readyRender = true;
    }

    @property
    _p7: Vec3 = new Vec3(1, 1, 1);
    @property
    get p7(): Vec3 { return this._p7; }
    set p7(value: Vec3) {
        this._p7.set(value); this.readyRender = true;
    }

    private readyRender = true;

    /**
     * 根据生成立方体演化，可缩放立方体任一顶点x/y/z坐标
     * @param options 参数选项。
     */
    private box(options?: IBoxOptions): primitives.IGeometry {
        options = options || {};

        const hw = (options.width || 1) / 2;
        const hh = (options.height || 1) / 2;
        const hl = (options.length || 1) / 2;

        const corners = [
            Vec3.set(c0, -hw, -hh, hl).multiply(this.p0),
            Vec3.set(c1, hw, -hh, hl).multiply(this.p1),
            Vec3.set(c2, hw, hh, hl).multiply(this.p2),
            Vec3.set(c3, -hw, hh, hl).multiply(this.p3),
            Vec3.set(c4, hw, -hh, -hl).multiply(this.p4),
            Vec3.set(c5, -hw, -hh, -hl).multiply(this.p5),
            Vec3.set(c6, -hw, hh, -hl).multiply(this.p6),
            Vec3.set(c7, hw, hh, -hl).multiply(this.p7)
        ];

        const faceAxes = [
            [2, 3, 1, 0], // FRONT
            [4, 5, 7, 6], // BACK
            [7, 6, 2, 3], // TOP
            [1, 0, 4, 5], // BOTTOM
            [1, 4, 2, 7], // RIGHT
            [5, 0, 6, 3],  // LEFT
        ];

        const faceNormals = [
            [0, 0, 1], // FRONT
            [0, 0, -1], // BACK
            [0, 1, 0], // TOP
            [0, -1, 0], // BOTTOM
            [1, 0, 0], // RIGHT
            [-1, 0, 0],  // LEFT
        ];

        const positions: number[] = [];
        const normals: number[] = [];
        const uvs: number[] = [];
        const indices: number[] = [];
        const minPos = new Vec3(-hw, -hh, -hl);
        const maxPos = new Vec3(hw, hh, hl);
        const boundingRadius = Math.sqrt(hw * hw + hh * hh + hl * hl);

        function _buildPlane(side: number) {
            const uSegments = 1;
            const vSegments = 1;

            let u: number;
            let v: number;
            let ix: number;
            let iy: number;
            const offset = positions.length / 3;
            const faceAxe = faceAxes[side];
            const faceNormal = faceNormals[side];

            for (iy = 0; iy <= vSegments; iy++) {
                for (ix = 0; ix <= uSegments; ix++) {
                    u = ix / uSegments;
                    v = iy / vSegments;

                    r.set(corners[faceAxe[u + v * 2]]);

                    positions.push(r.x, r.y, r.z);
                    normals.push(faceNormal[0], faceNormal[1], faceNormal[2]);
                    uvs.push(u, v);

                    if ((ix < uSegments) && (iy < vSegments)) {
                        const useg1 = uSegments + 1;
                        const a = ix + iy * useg1;
                        const b = ix + (iy + 1) * useg1;
                        const c = (ix + 1) + (iy + 1) * useg1;
                        const d = (ix + 1) + iy * useg1;

                        indices.push(offset + a, offset + d, offset + b);
                        indices.push(offset + b, offset + d, offset + c);
                    }
                }
            }
        }

        _buildPlane(0); // FRONT
        _buildPlane(4); // RIGHT
        _buildPlane(1); // BACK
        _buildPlane(5); // LEFT
        _buildPlane(3); // BOTTOM
        _buildPlane(2); // TOP

        return {
            positions,
            normals,
            uvs,
            indices,
            minPos,
            maxPos,
            boundingRadius,
        };
    }

    private mesh: Mesh = new Mesh();
    private createMesh() {
        const b = this.box();
        utils.createMesh(b, this.mesh);
        return this.mesh;
    }

    public render() {
        this.readyRender = false;
        const render = this.getComponent(MeshRenderer)!;
        if (render.mesh && render.mesh.isValid) render.mesh.destroy();
        render.mesh = this.createMesh();
    }

    start(){
        // window.sss = this;
    }

    update() {
        if (!this.readyRender) return;
        this.render();
    }
}
